'''A module designed to check for clones on join and on /clones.'''
############################################################################
#    Copyright (C) 2006 by Robert Small                                    #
#    SmallR2002@Gmail.com                                                  #
#                                                                          #
#    This program is free software; you can redistribute it and#or modify  #
#    it under the terms of the GNU General Public License as published by  #
#    the Free Software Foundation; either version 2 of the License, or     #
#    (at your option) any later version.                                   #
#                                                                          #
#    This program is distributed in the hope that it will be useful,       #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of        #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
#    GNU General Public License for more details.                          #
#                                                                          #
#    You should have received a copy of the GNU General Public License     #
#    along with this program; if not, write to the                         #
#    Free Software Foundation, Inc.,                                       #
#    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             #
############################################################################

__module_name__        = "bcclones"
__module_version__     = "2.0.0"
__module_description__ = "Detects clones on join (second version)."

# 
# 0.0.2  Thanks to freenode for causing this module to break unceremoniously
#       due to their nice habit of giving out cloaks which lack dots.
# 
# 0.0.1  Basics.
# 

import xchat
import string #IGNORE:W0402
      
class user:
    '''
    User data type.
    '''
    nick = ""
    host = ""
    prefix = ""
    def __init__(self):
        '''
        Making pylint happy.
        '''
    def hostname(self):
        '''
        Returns the hostname from the 'host'.
        '''
        return self.host[:self.host.find("@") + 1]
    def username(self):
        '''
        Returns the username of the subject.
        '''
        return self.host[self.host.find("@"):]

def clone_detect2(word, word_eol, userdata): #IGNORE:W0613
    '''
    Run detection against clones.
    @param word: all the data you need to work with, chopped up into small parts.
    @type word: string
    @param word_eol: all the data you need to work with, chopped from wherever you're starting from.
    @type word_eol: string
    @param userdata: custom object to pass back.
    @type userdata: object
    '''
    c = check_list(xchat.get_list("users"))
    
    if (len(word) == 2):
        temproom = word[1]
    else:
        temproom = xchat.get_info('channel')
    
    if c != None and (len(c[0][0]) > 0 or len(c[1][0]) > 0 or len(c[2][0]) > 0):
        if len(c[0][0]) > 0:
            for i, w in enumerate(c[0][1]):
                xchat.prnt("Clones detected in \x0305" + temproom + " " + c[0][0][i].nick + " " + w.nick + "\x03 on " + w.host)
        if len(c[1][0]) > 0:
            for i, w in enumerate(c[1][1]):
                xchat.prnt("Clones detected in \x0305" + temproom + " " + c[1][0][i].nick + " " + w.nick + "\x03 on *@" + c[1][0][i].host.split("@")[1])
        if len(c[2][0]) > 0:
            for i, w in enumerate(c[2][1]):
                if (c[2][0][i].host.count(".") > 0 and w.host.count(".") > 0) and (c[2][0][i].host[c[2][0][i].host.find("."):] == w.host[w.host.find("."):]):
                    xchat.prnt("Clones detected in \x0305" + temproom + " " + c[2][0][i].nick + " " + w.nick + "\x03 on *@*." + w.host[w.host.find("/"):])
                elif (c[2][0][i].host.count("/") > 0 and w.host.count("/") > 0) and (c[2][0].host[c[2][0][i].host.find("/"):] == w.host[w.host.find("/"):]):
                    xchat.prnt("Clones detected in \x0305" + temproom + " " + c[2][0][i].nick + " " + w.nick + "\x03 on *@*/" + w.host[w.host.find("/"):])
    return xchat.EAT_XCHAT

def clone_detect(word, word_eol, userdata): #IGNORE:W0613
    '''
    Detect clones, unfortunate *doubles*.
    @param word: all the data you need to work with, chopped up into small parts.
    @type word: string
    @param word_eol: all the data you need to work with, chopped from wherever you're starting from.
    @type word_eol: string
    @param userdata: custom object to pass back.
    @type userdata: object
    '''
    hosts = []
    for l in xchat.get_list("users"):
        if hosts.count(l.host) == 0:
            hosts.append(l.host)
    
    users = [[[], [], []]for i in range(len(hosts))]
    
    for i, h in enumerate(hosts):
        for l in xchat.get_list("users"):
            u = user()
            u.host = h
            r = check_users(l, u)
            if r != None:
                users[i][r].append(l.nick)
    
    for i, w in enumerate(users):
        if (len(w[0]) + len(w[1]) + len(w[2])) > 1:
            clones = "Clones detected on \x0305" + hosts[i] + "\x03: "
            if len(w[0]) > 0:
                clones = clones + "\x0305\x02" + string.join(w[0], "\x03\x02, \x0305\x02") + "\x03\x02"
            if len(w[1]) > 0:
                if (len(w[0]) > 0):
                    clones = clones + ", "
                clones = clones + "\x0305" + string.join(w[1], "\x03, \x0305") + "\x03"
            if len(w[2]) > 0:
                if (len(w[0]) > 0 or len(w[1]) > 0):
                    clones = clones + ", "
                clones = clones + "\x0303" + string.join(w[2], "\x03, \x0303") + "\x03"
            xchat.prnt(clones)
        
    return xchat.EAT_XCHAT

def join_clone_check(word, word_eol, userdata): #IGNORE:W0613
    '''
    Detect entering clones.
    @param word: all the data you need to work with, chopped up into small parts.
    @type word: string
    @param word_eol: all the data you need to work with, chopped from wherever you're starting from.
    @type word_eol: string
    @param userdata: custom object to pass back.
    @type userdata: object
    '''
    
    users = xchat.get_list("users") #Solid state, just incase.
    
    l = user()
    l.nick = word[0]
    l.host = word[2]
    
    c = check_list_against_user(users, l)
    
    if c != None and ((len(c[0][0]) > 0) or (len(c[1][0]) > 0) or (len(c[2][0]) > 0)):
        clones_warning = "Clones of \x02\x0305" + word[0] + "!" + word[2] + "\x02\x03 detected. "
        if len(c[0][0]) > 0:
            clones_warning = clones_warning + "Found on \x02\x0305nick!user@host\x02\x03: \x02\x0305"
            for w in c[0][0]:
                clones_warning = clones_warning + " " + w.nick
            clones_warning = clones_warning + "\x02\x03. "
        if len(c[1][0]) > 0:
            clones_warning = clones_warning + "Found on \x02\x0305nick!*@host\x02\x03: \x02\x0305"
            for w in c[1][0]:
                clones_warning = clones_warning + " " + w.nick
            clones_warning = clones_warning + "\x02\x03. "
        if len(c[2][0]) > 0:
            clones_warning = clones_warning + "Found on \x02\x0305nick!user@*.host\x02\x03: \x02\x0305"
            for w in c[2][0]:
                clones_warning = clones_warning + " " + w.nick
            clones_warning = clones_warning + "\x02\x03. "
        xchat.prnt(clones_warning)
        
    return xchat.EAT_NONE
    
def check_list(users):
    '''
    Check a set of users against each other.
    @param users: the users to check.
    @type users: user[]
    '''
    c = [[[], []], [[], []], [[], []]]
    for l in users[len(users)//2:]:
        for m in users[:len(users)//2]:
            r = check_users(l, m)
            if r != None:
                c[r][0].append(l)
                c[r][1].append(m)
    return c

def check_list_against_user(users, tuser):
    '''
    Check user against users.
    @param users: user list.
    @type users: user[]
    @param tuser: the user to compare against.
    @type tuser: user
    '''
    c = [[[], []], [[], []], [[], []]]
    for m in users:
        r = check_users(m, tuser)
        if r != None:
            c[r][0].append(m)
            c[r][1].append(tuser)
    return c

def check_users(user1, user2):
    '''
    Compare user1 against user2.
    @param user1: first user.
    @type user1: user
    @param user2: second user.
    @type user2: user
    '''
    if user1.host == user2.host:
        return 0
    if user1.host[user1.host.find("@")+1:] == user2.host[user2.host.find("@")+1:]:
        return 1
    if (user1.host[:user1.host.find("@")] == user2.host[:user2.host.find("@")]):
        if (user1.host.count(".") > 0 and user2.host.count(".") > 0) and (user1.host[user1.host.find("."):] == user2.host[user2.host.find("."):]):
            return 2
        elif (user1.host.count("/") > 0 and user2.host.count("/") > 0) and (user1.host[user1.host.find("/"):] == user2.host[user2.host.find("/"):]):
            return 2
    return None
    